import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;

public class example {

    // 指定加密算法为RSA
    private static String ALGORITHM = "RSA";

    // TODO 测试使用的用户名
    private static String USERNAME = "admin";

    // 指定公钥存放文件和私钥存放文件，和USERNAME是配对的
    // TODO 使用时修改为对应的公钥
    private static String PUBLIC_KEY = """
            -----BEGIN RSA Public Key-----
            MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq73m80px1jTDJ/YH/D2kGeGG0
            U/+oED+aHtJfIHF1r9xIzYGIQVJL+MOvRgSMC3w4P2feGUzeC0kGyL65IAYWkOTl
            vQbPSw975PXRMM7wne/L0w/cMu1ULumKec8JLptpknpENHHERzZF1MvzrKIBqXjF
            3Aa0lk0M1aOT9z6hnQIDAQAB
            -----END RSA Public Key-----""";

    // 请求url
    // TODO 注意修改该实际的地址
    private static String url = "http://192.168.1.243:20946/api/v1/user/token/generate";

    // 调用方法举例
    public static void main(String[] args) {
        // 生成加密参数
        String en_params = String.format("{\"username\":\"%s\",\"time\":%d,\"external_uid\":\"\"}",
                USERNAME, System.currentTimeMillis() / 1000);
        System.out.println("明文字符串：" + en_params);

        // 加密
        String encrypt_text = "";
        try {
            RSAPublicKey pubkey = fmtPublicKey(PUBLIC_KEY);
            encrypt_text = encrypt(en_params, pubkey);
            System.out.println("加密之后的字符串为：" + encrypt_text);
        } catch (Exception e) {
            System.out.println("加解密过程中发生错误：" + e.getMessage());
            return;
        }

        // 请求接口
        String body = String.format("{\"username\":\"%s\", \"encrypt\": \"%s\"}", USERNAME, encrypt_text);
        System.out.println("请求体：" + body);
        try {
            String resp = sendPost(url, body);
            System.out.println("接口返回值：" + resp);
        } catch (Exception e) {
            System.out.println("访问接口异常：" + e.getMessage());
            return;
        }
    }

    private static RSAPublicKey fmtPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        publicKey = publicKey.replace("-----BEGIN RSA Public Key-----", "");
        publicKey = publicKey.replace("-----END RSA Public Key-----", "");
        publicKey = publicKey.replaceAll("\n", "");
        publicKey = publicKey.trim();
        System.out.println("格式化后的key：" + publicKey);
        // 通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(decode(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }

    private static String encrypt(String plainText, RSAPublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return encode(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, plainText.getBytes("UTF-8"),
                    publicKey.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + plainText + "]时遇到异常", e);
        }
    }

    private static String encode(final byte[] Encp) {
        return Base64.getEncoder().encodeToString(Encp);
    }

    private static byte[] decode(final String Decp) {
        return Base64.getMimeDecoder().decode(Decp);
    }

    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) throws IOException {
        int maxBlock = 0;
        if (opmode == Cipher.DECRYPT_MODE) {
            maxBlock = keySize / 8;
        } else {
            maxBlock = keySize / 8 - 11;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            int offSet = 0;
            byte[] buff;
            int i = 0;
            while (datas.length > offSet) {
                if (datas.length - offSet > maxBlock) {
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                } else {
                    buff = cipher.doFinal(datas, offSet, datas.length - offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        } catch (Exception e) {
            throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
        }

        byte[] resultDatas = out.toByteArray();
        try {
            out.close();
        } catch (IOException e) {
            throw new RuntimeException("close error", e);
        }
        return resultDatas;
    }

    private static String sendPost(String url, String Params) throws IOException {
        OutputStreamWriter out = null;
        BufferedReader reader = null;
        String response = "";
        try {
            // 创建URL HTTP URL类 用这个类来创建连接
            URL httpUrl = new URL(url);
            // 建立连接
            HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("connection", "keep-alive");
            conn.setUseCaches(false);// 设置不要缓存
            conn.setInstanceFollowRedirects(true);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();

            // POST请求
            out = new OutputStreamWriter(conn.getOutputStream());
            out.write(Params);
            out.flush();

            // 读取响应
            reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String lines;
            while ((lines = reader.readLine()) != null) {
                lines = new String(lines.getBytes(), "utf-8");
                response += lines;
            }

            // 断开连接
            reader.close();
            conn.disconnect();
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常！" + e);
            e.printStackTrace();
        }
        finally {
            // 使用finally块来关闭输出流、输入流
            try {
                if (out != null) {
                    out.close();
                }
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        return response;
    }
}